/*
 * Copyright 2009-2010 Belmont Software Services
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.belmont.backup;

import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.zip.*;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.event.TreeModelListener;
import org.json.*;
import java.sql.*;

public class DatabaseManifest implements IBackupConstants {
    String includeFilters[];
    String excludeFilters[];
    FileDatabase db;
    MessageDigest md;
    Producer producer;

    public DatabaseManifest(FileDatabase db) {
	this.db = db;
	try {
	    md = Utils.getDigest(DIGEST_ALGORITHM);
	} catch (IOException ex) {
	    ex.printStackTrace();
	}
	producer = Producer.getInstance();
    }

    public void printIndex(String id) throws SQLException {
    }

    public int createIndex(String id, String client, String comment, File directory, FileStorage storage) throws SQLException, InterruptedException {
	int total = 0;

	if (! directory.isDirectory()) {
	    return 0;
	}
	//db.beginTransaction();
	try {
	    db.addBackup(id, client, new java.sql.Date(System.currentTimeMillis()), null, directory.getAbsolutePath(), null, comment);
	    total = createIndex1(id, new File(directory.getParent()), directory.getName()+"/", storage);
	    //db.commitTransaction();

	    return total;
	} catch (SQLException ex) {
	    ex.printStackTrace();
	    //db.abortTransaction();
	    throw ex;
	}
    }

    public void setFilters(String includeFilters[], String excludeFilters[]) {
	this.includeFilters = includeFilters;
	this.excludeFilters = excludeFilters;
    }

    /**
     * Calculates the checksum for a file if the last known checksum
     * doesn't match the file size and last modified date. In order to
     * calculate the size and last mod date we query the database for
     * the last backup for the given backup path and then we query for
     * a backup entry for that backup that matches the path we want to
     * checksum. If the queries succeed then we compare the size and
     * mod date against the given file f and return the pre-calcualted
     * checksum if they all match.
     */
    String checksumFile(File parent, String path, File f, MessageDigest md) throws InterruptedException {
	String checksum = null;

	try {
	    int pathid = db.getPath(path);
	    int idx = path.indexOf('/');

	    if (pathid != -1) {
		// XXX: would be nice not to have to do substring for
		// the backup path...
		String backuppath = parent.getAbsolutePath()+"/"+path.substring(0, idx);
		ResultSet rs = db.getBackupsForPath(backuppath);

		try {
		    long fsize = f.length();
		    long ftime = f.lastModified();
		    int backupid = -1;

		    while (rs.next()) {
			backupid = rs.getInt(1);
			ResultSet ms = db.getChecksumModTimes(backupid, path);

			if (ms == null) {
			    continue;
			}

			try {
			    while (ms.next()) {
				String ck = ms.getString(1);
				long size = ms.getLong(2);
				java.sql.Date mod = ms.getDate(3);

				if (fsize == size && mod.getTime() == ftime) {
				    return ck;
				}
			    }
			} finally {
			    ms.close();
			}
		    }
		} finally {
		    rs.close();
		}
	    }
	} catch (SQLException ex) {
	    // if we get database errors we're just going to ignore
	    // them and recalculate the checksum
	    ex.printStackTrace();
	}

	// we're here because the db queries failed or the file size
	// and mod date didn't match
	return Utils.checksum(f, md);
    }

    int createIndex1(String id, File parent, String path, FileStorage storage) throws SQLException, InterruptedException {
	int total = 0;

	File file = new File(parent+File.separator+path);
	String list[] = file.list();
	if (list == null) {
	    return total;
	}
	Status status = Status.getInstance();
	status.setCurrentOperation("Scanning");
	for (int i=0; i < list.length; i++) {
	    File f = new File(file, list[i]);

	    Utils.checkCancel();
	    String newpath = path+list[i]+((f.isDirectory()) ? "/" : "");
	    if (f.isDirectory()) {
		total += createIndex1(id, parent, newpath, storage);
	    } else {
		if (includeFilters != null && !Utils.matchFilter(list[i], includeFilters)) {
		    continue;
		}

		if (Utils.matchFilter(list[i], excludeFilters)) {
		    continue;
		}

		total++;
		//producer.sendNotify(this, NOTIFY_SCANNING_START, newpath);
		status.setCurrentFile(newpath);
		String checksum = checksumFile(parent, newpath, f, md);
		if (checksum == null) {
		    addFileError(id, newpath, "Error calculating checksum", null);
		} else {
		    try {
			if (storage != null) {
			    SFile sf = storage.getFileCreate(checksum);

			    if (sf != null) {
				sf.addExternalFile(f.getAbsolutePath());
			    }
			}
			db.addBackupEntry(id, checksum, newpath, f.length(), new java.sql.Date(f.lastModified()));
		    } catch (IOException ex) {
			addFileError(id, newpath, "Error creating file in storage", ex);
		    }
		}
	    }
	}

	return total;
    }

    void addFileError(String id, String path, String msg, Throwable t) {
	try {
	    if (t != null) {
		t.printStackTrace();
	    }
	    db.addBackupError(id, FILE_ACCESS_ERROR, path,
			      new java.sql.Date(System.currentTimeMillis()),
			      (t != null) ? (msg+": "+t.toString()) : msg);
	    producer.sendNotify(this, NOTIFY_ERROR_FILE, path);
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }

    public static void main(String args[]) throws Exception {
	FileDatabase db = new FileDatabase();
	Hashtable<String, String> props = new Hashtable<String, String>();
	props.put("backup.database.name", "backuptest.db");
	db.init(props);

	DatabaseManifest dbm = new DatabaseManifest(db);

	for (int i=0; i < args.length; i++) {
	    System.out.println("args["+i+"] = "+args[i]);
	}
	dbm.createIndex(args[0], args[1], "test backup", new File(args[2]), null);

	ResultSet rs = db.getBackupEntries(args[0]);

	int l = 0;
	try {
	    while (rs.next()) {
		System.out.println(rs.getString(1)+" "+rs.getString(2)+" "+rs.getLong(3)+" "+new java.util.Date(rs.getLong(4)).toString());
		l++;
	    }
	    System.out.println(l+" entries");
	} finally {
	    rs.close();
	}
    }
}